package hunan.qianyuan.codefatherback.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import hunan.qianyuan.codefatherback.ai.AiCodeGenTypeRoutingService;
import hunan.qianyuan.codefatherback.ai.AiCodeGenTypeRoutingServiceFactory;
import hunan.qianyuan.codefatherback.ai.AiCodeGeneratorServiceFactory;
import hunan.qianyuan.codefatherback.ai.model.enums.CodeGenTypeEnum;
import hunan.qianyuan.codefatherback.constant.AppConstant;
import hunan.qianyuan.codefatherback.core.AiCodeGeneratorFacade;
import hunan.qianyuan.codefatherback.core.builder.VueProjectBuilder;
import hunan.qianyuan.codefatherback.core.handler.StreamHandlerExecutor;
import hunan.qianyuan.codefatherback.exception.BusinessException;
import hunan.qianyuan.codefatherback.exception.ErrorCode;
import hunan.qianyuan.codefatherback.exception.ThrowUtils;
import hunan.qianyuan.codefatherback.model.dto.app.AppAddRequest;
import hunan.qianyuan.codefatherback.model.dto.app.AppQueryRequest;
import hunan.qianyuan.codefatherback.model.entity.App;
import hunan.qianyuan.codefatherback.mapper.AppMapper;
import hunan.qianyuan.codefatherback.model.entity.User;
import hunan.qianyuan.codefatherback.model.enums.ChatHistoryMessageTypeEnum;
import hunan.qianyuan.codefatherback.model.vo.AppVO;
import hunan.qianyuan.codefatherback.model.vo.UserVO;
import hunan.qianyuan.codefatherback.service.AppService;
import hunan.qianyuan.codefatherback.service.ChatHistoryService;
import hunan.qianyuan.codefatherback.service.ScreenshotService;
import hunan.qianyuan.codefatherback.service.UserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 应用 服务层实现。
 *
 * @author qianyaun
 */
@Service
@Slf4j
public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService {
    @Resource
    private UserService userService;
    @Resource
    private AiCodeGeneratorFacade aiCodeGeneratorFacade;
    @Resource
    private ChatHistoryService chatHistoryService;
    @Resource
    private StreamHandlerExecutor streamHandlerExecutor;
    @Resource
    private VueProjectBuilder vueProjectBuilder;
    @Resource
    private ScreenshotService screenshotService;
    @Resource
    @Qualifier("routingChatModelPrototype")
    private AiCodeGenTypeRoutingServiceFactory aiCodeGenTypeRoutingServiceFactory;

    @Override
    public Long createApp(AppAddRequest appAddRequest, User loginUser) {
        // 参数校验
        String initPrompt = appAddRequest.getInitPrompt();
        ThrowUtils.throwIf(StrUtil.isBlank(initPrompt), ErrorCode.PARAMS_ERROR, "初始化 prompt 不能为空");
        // 构造入库对象
        App app = new App();
        BeanUtil.copyProperties(appAddRequest, app);
        app.setUserId(loginUser.getId());
        // 应用名称暂时为 initPrompt 前 12 位
        app.setAppName(initPrompt.substring(0, Math.min(initPrompt.length(), 12)));
        // 使用 AI 智能选择代码生成类型
        AiCodeGenTypeRoutingService aiCodeGenTypeRoutingService = aiCodeGenTypeRoutingServiceFactory.createAiCodeGenTypeRoutingService();
        CodeGenTypeEnum selectedCodeGenType = aiCodeGenTypeRoutingService.routeCodeGenType(initPrompt);
        app.setCodeGenType(selectedCodeGenType.getValue());
        // 插入数据库
        boolean result = this.save(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        log.info("应用创建成功，ID: {}, 类型: {}", app.getId(), selectedCodeGenType.getValue());
        return app.getId();
    }

    @Override
    public Flux<String> chatToGenCode(Long appId, String message, User loginUser) {
//1.参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用不存在！");
        ThrowUtils.throwIf(StrUtil.isBlank(message), ErrorCode.PARAMS_ERROR, "用户消息不能为空");
//2.查询应用信息
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.PARAMS_ERROR, "应用不存在");
//3.验证用户是否有权限访问该应用，仅本人可以生成代码
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限访问该应用");
        }
        // 4. 获取应用的代码生成类型
        String codeGenTypeStr = app.getCodeGenType();
        CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenTypeStr);
        if (codeGenTypeEnum == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "不支持的代码生成类型");
        }
        // 5. 通过校验后，添加用户消息到对话历史
//        chatHistoryService.addChatMessage(appId, message, ChatHistoryMessageTypeEnum.USER.getValue(), loginUser.getId());
        // 6. 调用 AI 生成代码（流式）
//        Flux<String> contentFlux = aiCodeGeneratorFacade.generateAndSaveCodeStream(message, codeGenTypeEnum, appId);
        // 7. 收集AI响应内容并在完成后记录到对话历史
//        StringBuilder aiResponseBuilder = new StringBuilder();
        // 5. 通过校验后，添加用户消息到对话历史
        chatHistoryService.addChatMessage(appId, message, ChatHistoryMessageTypeEnum.USER.getValue(), loginUser.getId());
        // 6. 调用 AI 生成代码（流式）
        Flux<String> codeStream = aiCodeGeneratorFacade.generateAndSaveCodeStream(message, codeGenTypeEnum, appId);
        // 7. 收集 AI 响应内容并在完成后记录到对话历史
        return streamHandlerExecutor.doExecute(codeStream, chatHistoryService, appId, loginUser, codeGenTypeEnum);

    }

    @Override
    public String deployApp(Long appId, User loginUser) {
        // 1. 参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 不能为空");
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR, "用户未登录");

        // 2. 查询应用信息
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");

        // 3. 权限验证（仅所有者可部署）
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限部署该应用");
        }

        // 4. 生成或复用部署密钥（6位字母数字组合，确保唯一）
        String deployKey = app.getDeployKey();
        if (StrUtil.isBlank(deployKey)) {
            deployKey = generateUniqueDeployKey();
        }

        // 5. 构建源目录路径（使用Path API处理路径，更安全）
        CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(app.getCodeGenType());
        ThrowUtils.throwIf(codeGenTypeEnum == null, ErrorCode.SYSTEM_ERROR, "不支持的代码生成类型");

        Path sourceBaseDir = Paths.get(AppConstant.CODE_OUTPUT_ROOT_DIR);
        Path sourceDir = sourceBaseDir.resolve(codeGenTypeEnum.getValue() + "_" + appId);

        // 6. 检查源目录是否存在
        if (!Files.exists(sourceDir) || !Files.isDirectory(sourceDir)) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,
                    "应用代码不存在，请先生成代码（路径：" + sourceDir.toAbsolutePath() + "）");
        }

        // 7. Vue项目特殊处理：执行构建并验证结果
        if (CodeGenTypeEnum.VUE_PROJECT.equals(codeGenTypeEnum)) {
            log.info("开始构建Vue项目，源目录：{}", sourceDir.toAbsolutePath());
            boolean buildSuccess = vueProjectBuilder.buildProject(sourceDir.toString());
            ThrowUtils.throwIf(!buildSuccess, ErrorCode.SYSTEM_ERROR, "Vue项目构建失败，请检查代码和依赖");

            // 验证构建产物（dist目录及关键文件）
            Path distDir = sourceDir.resolve("dist");
            ThrowUtils.throwIf(!Files.exists(distDir), ErrorCode.SYSTEM_ERROR, "Vue构建未生成dist目录");

            Path indexHtml = distDir.resolve("index.html");
            ThrowUtils.throwIf(!Files.exists(indexHtml), ErrorCode.SYSTEM_ERROR, "dist目录中缺少index.html");

            sourceDir = distDir; // 切换部署源为dist目录
            log.info("Vue项目构建成功，部署源切换为：{}", sourceDir.toAbsolutePath());
        }

        // 8. 准备部署目录（清理旧文件，确保新鲜度）
        Path deployBaseDir = Paths.get(AppConstant.CODE_DEPLOY_ROOT_DIR);
        Path deployDir = deployBaseDir.resolve(deployKey);
        try {
            // 若部署目录已存在，先删除旧文件
            if (Files.exists(deployDir)) {
                log.warn("部署目录已存在，将清理旧文件：{}", deployDir.toAbsolutePath());
                FileUtil.del(deployDir.toFile());
            }
            // 复制源目录到部署目录
            FileUtil.copyContent(sourceDir.toFile(), deployDir.toFile(), true);
            log.info("文件复制完成，源：{} -> 目标：{}",
                    sourceDir.toAbsolutePath(), deployDir.toAbsolutePath());
        } catch (Exception e) {
            log.error("部署文件复制失败", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,
                    "部署失败：文件复制错误（" + e.getMessage() + "）");
        }

        // 9. 更新应用部署信息
        App updateApp = new App();
        updateApp.setId(appId);
        updateApp.setDeployKey(deployKey);
        updateApp.setDeployedTime(LocalDateTime.now());
        updateApp.setUpdateTime(LocalDateTime.now()); // 补充更新时间
        boolean updateResult = this.updateById(updateApp);
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新应用部署信息失败");
        // 10. 返回部署访问URL
        String deployUrl = String.format("%s/%s/", AppConstant.CODE_DEPLOY_HOST, deployKey);
        generateAppScreenshotAsync(appId, deployUrl);
        log.info("应用部署成功，访问地址：{}", deployUrl);
        return deployUrl;
    }

    @Override
    public void generateAppScreenshotAsync(Long appId, String appUrl) {
        Thread.startVirtualThread(() -> {
            String updateUrl = screenshotService.generateAndUploadScreenshot(appUrl);
            App updateApp = new App();
            updateApp.setId(appId);
            updateApp.setCover(updateUrl);
            boolean updateResult = this.updateById(updateApp);
            ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新应用封面失败");
        });

    }

    /**
     * 生成唯一的6位部署密钥（字母+数字）
     */
    private String generateUniqueDeployKey() {
        int retryCount = 0;
        while (retryCount < 5) { // 最多重试5次
            String key = RandomUtil.randomStringUpper(6); // 6位大写字母数字组合
            // 检查密钥是否已存在
            QueryWrapper wrapper = QueryWrapper.create().eq(App::getDeployKey, key);
            if (this.count(wrapper) == 0) {
                return key;
            }
            retryCount++;
            log.warn("部署密钥冲突，重试生成（{}次）", retryCount);
        }
        throw new BusinessException(ErrorCode.SYSTEM_ERROR, "生成部署密钥失败，请稍后重试");
    }

    @Override
    public AppVO AppConvertVO(App app) {
        if (app == null) {
            return null;
        }
        AppVO appVO = new AppVO();
        BeanUtils.copyProperties(app, appVO);
        Long userId = app.getUserId();
        ThrowUtils.throwIf(userId == null, ErrorCode.NOT_FOUND_ERROR, "用户不存在！");
        User user = userService.getById(userId);
        appVO.setUser(userService.getUserVO(user));
        return appVO;
    }

    @Override
    public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
        if (appQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = appQueryRequest.getId();
        String appName = appQueryRequest.getAppName();
        String cover = appQueryRequest.getCover();
        String initPrompt = appQueryRequest.getInitPrompt();
        String codeGenType = appQueryRequest.getCodeGenType();
        String deployKey = appQueryRequest.getDeployKey();
        Integer priority = appQueryRequest.getPriority();
        Long userId = appQueryRequest.getUserId();
        String sortField = appQueryRequest.getSortField();
        String sortOrder = appQueryRequest.getSortOrder();
        return QueryWrapper.create()
                .eq("id", id)
                .like("appName", appName)
                .like("cover", cover)
                .like("initPrompt", initPrompt)
                .eq("codeGenType", codeGenType)
                .eq("deployKey", deployKey)
                .eq("priority", priority)
                .eq("userId", userId)
                .orderBy(sortField, "ascend".equals(sortOrder));
    }

    /**
     * @param appList
     * @return
     */
    @Override
    public List<AppVO> getAppVoList(List<App> appList) {
        if (appList == null || appList.isEmpty()) {
            return new ArrayList<>();
        }
        List<AppVO> appVOList = appList.stream().map(this::AppConvertVO).toList();
        Set<Long> userIdSet = appList.stream().map(App::getUserId).collect(Collectors.toSet());
        Map<Long, List<UserVO>> IdByUserVoMap = userService.listByIds(userIdSet).stream().map(userService::getUserVO).collect(Collectors.groupingBy(UserVO::getId));
        appVOList.forEach(appVO -> {
            Long userId = appVO.getUserId();
            if (IdByUserVoMap.get(userId) != null) {
                appVO.setUser(IdByUserVoMap.get(userId).get(0));
            }
        });
        return appVOList;
    }

    /**
     * 删除应用时关联删除对话历史
     *
     * @param id 应用ID
     * @return 是否成功
     */
    @Override
    public boolean removeById(Serializable id) {
        if (id == null) {
            return false;
        }
        // 转换为 Long 类型
        Long appId = Long.valueOf(id.toString());
        if (appId <= 0) {
            return false;
        }
        // 先删除关联的对话历史
        try {
            chatHistoryService.deleteByAppId(appId);
        } catch (Exception e) {
            // 记录日志但不阻止应用删除
            log.error("删除应用关联对话历史失败: {}", e.getMessage());
        }
        // 删除应用
        return super.removeById(id);
    }

}
